package com.gavin.plugin.database.optimize.parse;

import java.sql.SQLException;
import java.util.ArrayList;

import com.gavin.model.Model;
import com.gavin.plugin.database.ext.MathExt;
import com.gavin.plugin.database.help.ShardingUtils;
import com.gavin.plugin.service.ITransBus;
import com.gavin.utils.Utility;

public class ParsedSQL {
	private String SQL;
	private String idMath;
	private ArrayList<String> paraNames;
	private ArrayList<String> dataGroupIds;
	private String tableName;
	private String dataGroupId;
	
	
	/**
	 * 分析SQL语法 {}之内的为参数名，需要替换成? {{代表{字符 }}代表}字符 strSQLType:
	 * insert,update,delete,selectfield,selectrecord,selectrecordset
	 */
	public static ParsedSQL parseSourceSQL(String strSourceSQL, Model modelRequest,ITransBus transBus) throws SQLException {
		String strIdParaName = null;
		ArrayList<String> alParaName = new ArrayList<String>();
		ArrayList<String> alDataGroupId = new ArrayList<String>();
		String strTableName = null;
		String strDataGroupId = null;

		StringBuilder strbSQL = new StringBuilder();

		int nState = 0;// 0 : {} 之外的字符, 1: {}之内字符.
		int nLength = strSourceSQL.length();

		StringBuilder strbParaName = new StringBuilder(nLength);
		int i = 0;
		while (i < nLength) {
			char ch = strSourceSQL.charAt(i);
			switch (ch) {
			case '\\':
				if (strSourceSQL.charAt(i + 1) == '\\') {// 字符\
					strbSQL.append("\\");
					i += 2;
					break;
				} else if (strSourceSQL.charAt(i + 1) == '{') {// 字符{
					strbSQL.append("{");
					i += 2;
					break;
				} else if (strSourceSQL.charAt(i + 1) == '}') {// 字符}
					strbSQL.append("}");
					i += 2;
					break;
				}
				break;

			case '{':
				if (nState > 0 && i + 1 < nLength) {// 为普通字符
					strbParaName.append(ch);
					nState++;
					i += 1;
					break;
				}

				// 字段开始
				nState++;
				i++;
				break;

			case '}':
				if (nState > 1 && i + 1 < nLength) {// 为普通字符
					strbParaName.append(ch);
					nState--;
					i++;
					break;
				}

				// 在{}之内
				nState--;
				if (nState > 0) {// 由于嵌套的原因，{}尚未结束
					strbSQL.append(ch);
					i++;
					break;
				}

				// {}已经结束
				if (strbParaName.length() == 0) {
					throw new SQLException("SQL syntax Error: " + strSourceSQL);
				}

				// 参数处理
				if (strbParaName.indexOf("m:") == 0) {// 使用数学表达式计算出来的值直接替换,Big
														// Table Name: select *
														// from
														// tbTable{m:lCompanyId*100/nCompanyCount}
					String strFieldId = strbParaName.substring(2);
					long lValue = MathExt.getMathValue(strFieldId, modelRequest);
					strbSQL.append(lValue);
				} else if (strbParaName.indexOf("TI:") == 0) {// 自动选择DataGroupId及数据库连接，并生成物理表名，Big
																// Table Name:
																// select * from
																// {TI:tbTable,lCompanyId}
					if (strTableName != null) {
						throw new SQLException(
								"SQL syntax Error, multiple (T|S|TI|SI|II)s are not allowed: " + strSourceSQL);
					}
					if (strIdParaName != null) {
						throw new SQLException(
								"SQL syntax Error, multiple (I|TI|SI|II)s are not allowed: " + strSourceSQL);
					}
					String[] strsParaName = Utility.splitString(strbParaName.substring(3), ',');
					if (strsParaName.length != 2) {
						throw new SQLException("SQL syntax Error: " + strSourceSQL);
					}
					for (int j = 0; j < strsParaName.length; j++) {
						strsParaName[j] = strsParaName[j].trim();
					}

					strTableName = strsParaName[0];
					strbSQL.append("{T:").append(strTableName).append('}');

					strIdParaName = strsParaName[1];
				} else if (strbParaName.indexOf("SI:") == 0) {// 自动选择DataGroupId及数据库连接，不涉及物理表名:
																// select * from
																// tbConfig
																// {SI:tbTable,lCompanyId}
					if (strTableName != null) {
						throw new SQLException(
								"SQL syntax Error, multiple (T|S|TI|SI|II)s are not allowed: " + strSourceSQL);
					}
					if (strIdParaName != null) {
						throw new SQLException(
								"SQL syntax Error, multiple (I|TI|SI|II)s are not allowed: " + strSourceSQL);
					}
					String[] strsParaName = Utility.splitString(strbParaName.substring(3), ',');
					if (strsParaName.length != 2) {
						throw new SQLException("SQL syntax Error: " + strSourceSQL);
					}
					for (int j = 0; j < strsParaName.length; j++) {
						strsParaName[j] = strsParaName[j].trim();
					}

					strTableName = strsParaName[0];
					strIdParaName = strsParaName[1];
				} else if (strbParaName.indexOf("II:") == 0) {// 自动选择DataGroupId及数据库连接，并生成物理表索引，:
																// select * from
																// tbTable{II:tbTable,lCompanyId}
					if (strTableName != null) {
						throw new SQLException(
								"SQL syntax Error, multiple (T|S|TI|SI|II)s are not allowed: " + strSourceSQL);
					}
					if (strIdParaName != null) {
						throw new SQLException(
								"SQL syntax Error, multiple (I|TI|SI|II)s are not allowed: " + strSourceSQL);
					}
					String[] strsParaName = Utility.splitString(strbParaName.substring(3), ',');
					if (strsParaName.length != 2) {
						throw new SQLException("SQL syntax Error: " + strSourceSQL);
					}
					for (int j = 0; j < strsParaName.length; j++) {
						strsParaName[j] = strsParaName[j].trim();
					}

					strTableName = strsParaName[0];
					strIdParaName = strsParaName[1];

					int nTableIndex = ShardingUtils.selectTable(transBus.getTableRule(strTableName),modelRequest, strTableName, strIdParaName);
					strbSQL.append(nTableIndex);
				}
				/*
				 * else if(strbParaName.indexOf("T:")==0) {//替换成真正的表名，Big Table
				 * Name: select * from {T:tbTable} if(strTableName!=null) {
				 * throw new
				 * SQLException("SQL syntax Error, multiple (T|S|TI|SI)s are not allowed: "
				 * +strSourceSQL); } strTableName=strbParaName.substring(2);
				 * strbSQL.append('{').append(strbParaName).append('}'); } else
				 * if(strbParaName.indexOf("I:")==0) {//用于定位记录，被lId替换，Id字段:
				 * where {I:lId}=1 if(strIdParaName!=null) { throw new
				 * SQLException("SQL syntax Error, multiple (I|TI|SI)s are not allowed: "
				 * +strSourceSQL); } strIdParaName=strbParaName.substring(2);
				 * alParaName.add(strIdParaName);
				 * strbSQL.append('{').append(strbParaName.substring(2)).append(
				 * '}'); }
				 */ else if (strbParaName.indexOf("ai:") == 0) {// 将{}内的内容替换成对应物理表的表名:
																// select * from
																// {ai:tbTable,lCompanyId}
					String[] strsParaName = Utility.splitString(strbParaName.substring(3), ',');
					if (strsParaName.length != 2) {
						throw new SQLException("SQL syntax Error: " + strSourceSQL);
					}
					for (int j = 0; j < strsParaName.length; j++) {
						strsParaName[j] = strsParaName[j].trim();
					}

					int nTableIndex = ShardingUtils.selectTable(transBus.getTableRule(strsParaName[0]),modelRequest, strsParaName[0], strsParaName[1]);
					strbSQL.append(strsParaName[0] + nTableIndex);
				} else if (strbParaName.indexOf("ti:") == 0) {// 将{}内的内容替换成物理表的索引:
																// select * from
																// tbTable{ti:tbTable,lCompanyId}
					String[] strsParaName = Utility.splitString(strbParaName.substring(3), ',');
					if (strsParaName.length != 2) {
						throw new SQLException("SQL syntax Error: " + strSourceSQL);
					}
					for (int j = 0; j < strsParaName.length; j++) {
						strsParaName[j] = strsParaName[j].trim();
					}

					int nTableIndex = ShardingUtils.selectTable(transBus.getTableRule(strsParaName[0]),modelRequest, strsParaName[0], strsParaName[1]);
					strbSQL.append(nTableIndex);
				} else if (strbParaName.indexOf("di:") == 0) {// 将{}内的内容替换成对应物理库的索引:
																// select * from
																// Database{di:tbTable,lCompanyId}.tbConfig
					String[] strsParaName = Utility.splitString(strbParaName.substring(3), ',');
					if (strsParaName.length != 2) {
						throw new SQLException("SQL syntax Error: " + strSourceSQL);
					}
					for (int j = 0; j < strsParaName.length; j++) {
						strsParaName[j] = strsParaName[j].trim();
					}

					int nDatabaseIndex = ShardingUtils.selectDatabase(transBus.getTableRule(strsParaName[0]),modelRequest, strsParaName[0], strsParaName[1]);
					strbSQL.append(nDatabaseIndex);
				} else if (strbParaName.indexOf("S:") == 0) {// 选择一个数据库连接执行SQL，优先使用上次使用的数据库连接:
																// select
																// last_insert_id()
																// {S:tbTable}
					if (strTableName != null) {
						throw new SQLException(
								"SQL syntax Error, multiple (T|S|TI|SI|II)s are not allowed: " + strSourceSQL);
					}
					strTableName = strbParaName.substring(2);
				} else if (strbParaName.indexOf("DG:") == 0) {// 直接选择DataGroupId，select
																// * from tbTest
																// {DG:readDemo}
					strDataGroupId = strbParaName.substring(3);
				} else {// 普通参数
					strbSQL.append('{').append(strbParaName).append('}');
					alParaName.add(strbParaName.toString());
				}

				strbParaName.setLength(0);

				i++;
				break;
			default:
				if (nState == 0) {// 在{}之外
					strbSQL.append(ch);
				} else {
					strbParaName.append(ch);
				}
				i++;
				break;
			}
		}

		if (nState != 0) {
			throw new SQLException("SQL syntax Error: " + strSourceSQL);
		}

		ParsedSQL parsedSQL = new ParsedSQL();
		parsedSQL.setSQL(strbSQL.toString());
		parsedSQL.setIdMath(strIdParaName);
		parsedSQL.setParaNames(alParaName);
		parsedSQL.setDataGroupIds(alDataGroupId);
		parsedSQL.setTableName(strTableName);
		parsedSQL.setDataGroupId(strDataGroupId);

		return parsedSQL;
	}

	public String getSQL() {
		return SQL;
	}
	public String getIdMath() {
		return idMath;
	}
	public ArrayList<String> getParaNames() {
		return paraNames;
	}
	public ArrayList<String> getDataGroupIds() {
		return dataGroupIds;
	}
	public String getTableName() {
		return tableName;
	}
	public String getDataGroupId() {
		return dataGroupId;
	}
	public void setSQL(String sQL) {
		SQL = sQL;
	}
	public void setIdMath(String idMath) {
		this.idMath = idMath;
	}
	public void setParaNames(ArrayList<String> paraNames) {
		this.paraNames = paraNames;
	}
	public void setDataGroupIds(ArrayList<String> dataGroupIds) {
		this.dataGroupIds = dataGroupIds;
	}
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}
	public void setDataGroupId(String dataGroupId) {
		this.dataGroupId = dataGroupId;
	}
}
